package net.pws.common.persistence;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.pws.common.domain.BusinessException;
import net.pws.common.domain.DomainAction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * 部分实现了Martin Fowler的Unit Of Work模式<br>
 * 其中持久化部分的逻辑通过PersistenceInstructionVisitor实现
 * 
 * @see PersistenceInstructionVisitor
 */
public class UnitOfWork {
    
    static final Log log = LogFactory.getLog(UnitOfWork.class);
    
    private static abstract class AbstractInstruction implements
                                                     PersistenceInstruction {
        protected Object entity;
        
        protected DomainAction validator = DomainAction.DUMMY;
        
        protected AbstractInstruction() {
        }
        
        public AbstractInstruction(Object entity) {
            if (null == entity) {
                throw new NullPointerException();
            }
            this.entity = entity;
        }
        
        public AbstractInstruction(Object entity, DomainAction validator) {
            if (null == entity) {
                throw new NullPointerException();
            }
            this.entity = entity;
            this.validator = validator;
        }
        
        public Object getEntity() {
            return entity;
        }
        
    }
    
    private static class CreateEntityInstructionImpl extends
                                                    AbstractInstruction implements
                                                                       CreateEntityInstruction {
        
        public CreateEntityInstructionImpl(Object entity) {
            super(entity);
        }
        
        public CreateEntityInstructionImpl(Object entity, DomainAction validator) {
            super(entity, validator);
        }
        
        public void accept(PersistenceInstructionVisitor visitor) {
            try {
                validator.execute();
                visitor.visit(this);
            }
            catch (BusinessException e) {
                throw new CreateEntityException(this.getEntity(), e);
            }
        }
        
    }
    
    private static class UpdateEntityInstructionImpl extends
                                                    AbstractInstruction implements
                                                                       UpdateEntityInstruction {
        
        public UpdateEntityInstructionImpl(Object entity) {
            super(entity);
        }
        
        public UpdateEntityInstructionImpl(Object entity, DomainAction validator) {
            super(entity, validator);
        }
        
        public void accept(PersistenceInstructionVisitor visitor) {
            try {
                validator.execute();
                visitor.visit(this);
            }
            catch (BusinessException e) {
                throw new UpdateEntityException(this.getEntity(), e);
            }
        }
        
    }
    
    private static class UnusedEntityInstructionImpl extends
                                                    AbstractInstruction implements
                                                                       UnusedEntityInstruction {
        
        public UnusedEntityInstructionImpl(Object entity) {
            super(entity);
        }
        
        public void accept(PersistenceInstructionVisitor visitor) {
            visitor.visit(this);
        }
        
    }
    
    private static class DeleteEntityInstructionImpl extends
                                                    AbstractInstruction implements
                                                                       DeleteEntityInstruction {
        
        public DeleteEntityInstructionImpl(Object entity) {
            super(entity);
        }
        
        public DeleteEntityInstructionImpl(Object entity, DomainAction validator) {
            super(entity, validator);
        }
        
        public void accept(PersistenceInstructionVisitor visitor) {
            try {
                validator.execute();
                visitor.visit(this);
            }
            catch (BusinessException e) {
                throw new DeleteEntityException(this.getEntity(), e);
            }
        }
        
    }
    
    private static class DeleteEntityByIdInstructionImpl implements
                                                        DeleteEntityByIdInstruction {
        
        private Class entityClazz;
        
        private Serializable entityId;
        
        private DeleteEntityByIdInstructionImpl(Class entityClazz,
                                                Serializable entityId) {
            this.entityClazz = entityClazz;
            this.entityId = entityId;
        }
        
        public Class getEntityClass() {
            return entityClazz;
        }
        
        public Serializable getEntityId() {
            return entityId;
        }
        
        public void accept(PersistenceInstructionVisitor visitor) {
            try {
                visitor.visit(this);
            }
            catch (BusinessException e) {
                throw new DeleteEntityByIdException(this.getEntityClass(),
                                                    this.getEntityId(),
                                                    e);
            }
        }
        
    }
    
    private static class OptimisticConcurrencyInstructionImpl implements
                                                             OptimisticConcurrencyInstruction {
        private Object target;
        
        private MergeCallback mergeCallback;
        
        private Object source;
        
        private OptimisticConcurrencyInstructionImpl(Object target,
                                                     MergeCallback mergeCallback,
                                                     Object source) {
            this.target = target;
            this.mergeCallback = mergeCallback;
            this.source = source;
        }
        
        public Object getTarget() {
            return target;
        }
        
        public MergeCallback getMergeCallback() {
            return mergeCallback;
        }
        
        public Object getSource() {
            return source;
        }
        
        public void accept(PersistenceInstructionVisitor visitor) {
            try {
                visitor.visit(this);
            }
            catch (BusinessException e) {
                throw new OptimisticConcurrencyException(this.getSource(), e);
            }
        }
        
    }
    
    /**
     * 清除内存的指令,可提高性能
     */
    private List unusedEntityInstructions = new ArrayList();
    
    /**
     * 包括了所有的乐观并发的指令
     */
    private List optimisticConcurrencyInstructions = new ArrayList();
    
    /**
     * 包括了CUD的指令
     */
    private List persistenceInstructions = new ArrayList();
    
    /**
     * CUD的指令执行后从内存清除
     */
    private List persistenceInstructions2Remove = new ArrayList();
    
    /**
     * 乐观并发优先
     */
    private boolean optimisticConcurrencyFirst = true;
    
    /**
     * 在UnitOfWork执行完后自动刷新
     */
    private boolean autoFlush = true;
    
    /**
     * 构造器
     */
    public UnitOfWork() {
    }
    
    /**
     * 乐观并发优先执行,然后再执行其他的增删改(非乐观并发)操作.
     */
    public UnitOfWork optimisticConcurrencyFirst() {
        optimisticConcurrencyFirst = true;
        return this;
    }
    
    /**
     * 乐观并发最后执行,其他的增删改(非乐观并发)操作优先执行,执行完后同时从内存中清除,然后再执行乐观并发,这种方式可以提高性能.
     */
    public UnitOfWork optimisticConcurrencyLast() {
        optimisticConcurrencyFirst = false;
        return this;
    }
    
    /**
     * 注册需要新建的实体
     * 
     * @param entity
     */
    public UnitOfWork registerNew(Object entity) {
        persistenceInstructions.add(new CreateEntityInstructionImpl(entity));
        persistenceInstructions2Remove.add(new UnusedEntityInstructionImpl(entity));
        return this;
    }
    
    /**
     * 注册需要新建的实体
     * 
     * @param entity
     */
    public UnitOfWork registerNew(Object entity, DomainAction validator) {
        persistenceInstructions.add(new CreateEntityInstructionImpl(entity,
                                                                    validator));
        persistenceInstructions2Remove.add(new UnusedEntityInstructionImpl(entity));
        return this;
    }
    
    /**
     * 注册需要新建的实体
     * 
     * @param entities
     */
    public UnitOfWork registerNew(List entities) {
        for (int i = 0; i < entities.size(); i++) {
            registerNew(entities.get(i));
        }
        return this;
    }
    
    /**
     * 注册需要修改的实体
     * 
     * @param entity
     */
    public UnitOfWork registerDirty(Object entity) {
        persistenceInstructions.add(new UpdateEntityInstructionImpl(entity));
        persistenceInstructions2Remove.add(new UnusedEntityInstructionImpl(entity));
        return this;
    }
    
    /**
     * 注册需要修改的实体
     * 
     * @param entity
     */
    public UnitOfWork registerDirty(Object entity, DomainAction validator) {
        persistenceInstructions.add(new UpdateEntityInstructionImpl(entity,
                                                                    validator));
        persistenceInstructions2Remove.add(new UnusedEntityInstructionImpl(entity));
        return this;
    }
    
    /**
     * 注册需要修改的实体
     * 
     * @param entities
     */
    public UnitOfWork registerDirty(List entities) {
        for (int i = 0; i < entities.size(); i++) {
            registerDirty(entities.get(i));
        }
        return this;
    }
    
    /**
     * 注册需要删除的实体
     * 
     * @param entity
     */
    public UnitOfWork registerRemoved(Object entity) {
        persistenceInstructions.add(new DeleteEntityInstructionImpl(entity));
        persistenceInstructions2Remove.add(new UnusedEntityInstructionImpl(entity));
        return this;
    }
    
    /**
     * 注册需要删除的实体
     * 
     * @param entity
     */
    public UnitOfWork registerRemoved(Object entity, DomainAction validator) {
        persistenceInstructions.add(new DeleteEntityInstructionImpl(entity,
                                                                    validator));
        persistenceInstructions2Remove.add(new UnusedEntityInstructionImpl(entity));
        return this;
    }
    
    /**
     * 注册需要删除的实体
     * 
     * @param entities
     */
    public UnitOfWork registerRemoved(List entities) {
        for (int i = 0; i < entities.size(); i++) {
            registerRemoved(entities.get(i));
        }
        return this;
    }
    
    /**
     * 注册需要删除的实体对应的class和id
     * 
     * @param entityClazz
     * @param entityId
     */
    public UnitOfWork registerRemovedById(Class entityClazz,
                                          Serializable entityId) {
        persistenceInstructions.add(new DeleteEntityByIdInstructionImpl(entityClazz,
                                                                        entityId));
        return this;
    }
    
    /**
     * 注册那些在本次工作单元中不需要使用的实体,在执行工作单元之前会首先从内存清除这些实体,通过这种方式可以提高性能(特别是大数据量的情况下)
     * 
     * @param entity
     */
    public UnitOfWork registerUnused(Object entity) {
        log.error("add unused:" + entity);
        persistenceInstructions.add(new UnusedEntityInstructionImpl(entity));
        return this;
    }
    
    /**
     * 注册需要修改的实体，但是该实体被并发修改，持久化层采用乐观锁定进行处理，在检查到版本合并失败的时候，需要使用者手工合并
     * 
     * @param entity
     * @param source
     * @param mergeCallback
     */
    public UnitOfWork registerDirty(Object entity, MergeCallback mergeCallback) {
        optimisticConcurrencyInstructions.add(new OptimisticConcurrencyInstructionImpl(entity,
                                                                                       mergeCallback,
                                                                                       entity));
        return this;
    }
    
    public void enableAuthoFlush() {
        this.autoFlush = true;
    }
    
    public void disableAutoFlush() {
        this.autoFlush = false;
    }
    
    public boolean isAutoFlush() {
        return this.autoFlush;
    }
    
    /**
     * 注册需要修改的实体，但是该实体被并发修改，持久化层采用乐观锁定进行处理，在检查到版本合并失败的时候，需要使用者手工合并
     * 
     * @param entity
     * @param source
     * @param mergeCallback
     */
    public UnitOfWork registerDirty(Object entity,
                                    MergeCallback mergeCallback,
                                    Object source) {
        optimisticConcurrencyInstructions.add(new OptimisticConcurrencyInstructionImpl(entity,
                                                                                       mergeCallback,
                                                                                       source));
        return this;
    }
    
    /**
     * @param unitOfWork
     */
    public UnitOfWork add(UnitOfWork unitOfWork) {
        unusedEntityInstructions.addAll(unitOfWork.unusedEntityInstructions);
        optimisticConcurrencyInstructions.addAll(unitOfWork.optimisticConcurrencyInstructions);
        persistenceInstructions.addAll(unitOfWork.persistenceInstructions);
        persistenceInstructions2Remove.addAll(unitOfWork.persistenceInstructions2Remove);
        return this;
    }
    
    /**
     * @param visitor
     * @param callback
     */
    public void execute(PersistenceInstructionVisitor visitor) {
        log.debug(this);
        executeUnusedEntityInstructions(visitor);
        if (optimisticConcurrencyFirst) {
            executeOptimisticConcurrencyInstructions(visitor);
            executePersistenceInstructions(visitor);
        }
        else {
            executePersistenceInstructions(visitor);
            executeOptimisticConcurrencyInstructions(visitor);
        }
    }
    
    /**
     * @param visitor
     */
    private void executeUnusedEntityInstructions(PersistenceInstructionVisitor visitor) {
        for (Iterator iterator = unusedEntityInstructions.iterator(); iterator.hasNext();) {
            PersistenceInstruction persistenceInstruction = (PersistenceInstruction) iterator.next();
            persistenceInstruction.accept(visitor);
        }
    }
    
    /**
     * @param visitor
     */
    private void executeOptimisticConcurrencyInstructions(PersistenceInstructionVisitor visitor) {
        for (Iterator iterator = optimisticConcurrencyInstructions.iterator(); iterator.hasNext();) {
            PersistenceInstruction persistenceInstruction = (PersistenceInstruction) iterator.next();
            persistenceInstruction.accept(visitor);
        }
    }
    
    /**
     * @param visitor
     */
    private void executePersistenceInstructions(PersistenceInstructionVisitor visitor) {
        for (Iterator iterator = persistenceInstructions.iterator(); iterator.hasNext();) {
            PersistenceInstruction persistenceInstruction = (PersistenceInstruction) iterator.next();
            persistenceInstruction.accept(visitor);
        }
    }
    
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("\noptimisticConcurrencyFirst:")
          .append(optimisticConcurrencyFirst)
          .append("\nunusedEntityInstructions:")
          .append(unusedEntityInstructions)
          .append("\noptimisticConcurrencyInstructions")
          .append(optimisticConcurrencyInstructions)
          .append("\npersistenceInstructions")
          .append(persistenceInstructions)
          .append("\npersistenceInstructions2Remove")
          .append(persistenceInstructions2Remove);
        return sb.toString();
    }
    
}
